package fun.ipconfig;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.security.ProtectionDomain;
import java.util.HashMap;
import java.util.Map;


public class MTransformer implements ClassFileTransformer {
    private static Instrumentation ins;
    private String packageName = "fun";
    static Map<String, Long> maps = new HashMap<>();

    public MTransformer() {
        reload();
        System.out.println("lisent-package: " + packageName);
    }

    public MTransformer(String packageName) {
        this.packageName = packageName;
        reload();
        System.out.println("lisent-package: " + packageName);
    }

    public static void setIns(Instrumentation ins) {
        MTransformer.ins = ins;
    }

    static void reload() {
        System.out.println("reload");
        new Thread(() -> {
            while (true) {
                check();
                try {
                    Thread.sleep(1000);
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        }).start();
    }

    static void check(){
        maps.keySet().forEach(f->{
            try {
                Class<?> c = Class.forName(f);
                String path = c.getResource("").getPath();
                String[] spit = f.split("[.]");
                String name = spit[spit.length - 1];

                String fileName = path + name + ".class";
                File file = new File(fileName);
               if(maps.get(f) == null){
                   maps.put(f, file.lastModified());
                   System.out.println(fileName + " first load");
               }
                if(maps.get(f) != file.lastModified()){
                    flushClass(c, fileName);
                    maps.put(f, file.lastModified());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
      
        });
    }

    static void flushClass(Class<?> c, String file) {
        FileInputStream inputStream = null;
        try {
            System.out.println("reload: " + file);
            inputStream = new FileInputStream(file);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int bufferSize = 1024;
            byte[] buffer = new byte[bufferSize];
            int length;
            while ((length = inputStream.read(buffer)) != -1) {
                bos.write(buffer, 0, length);
            }
            byte[] arr = bos.toByteArray();
            ClassDefinition cd = new ClassDefinition(c, arr);
            MTransformer.ins.redefineClasses(cd);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (UnmodifiableClassException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public byte[] transform(ClassLoader loader,
            String className,
            Class<?> classBeingRedefined,
            ProtectionDomain protectionDomain,
            byte[] classfileBuffer) throws IllegalClassFormatException {
        if(className.replace("/", ".").startsWith(packageName)){
            maps.put(className.replaceAll("/", "."), null);
            return classfileBuffer;
        }
        return classfileBuffer;
    }
}
